
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Zookeeper - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>Zookeeper - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">Zookeeper的核心功能可以被概括为什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">简单来说，Zookeeper可以被理解为“文件系统 + 监听通知机制”。它提供了一个类似于文件系统的树形结构（由znode组成），并允许客户端监控这些节点的变化，当节点变动时，客户端会收到通知。</div>
          </div>
          <div class="card-source">来源: 二、Zookeeper介绍</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">Zookeeper中的znode有哪四种主要类型？它们各自有什么特点？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">Zookeeper的znode有四种主要类型：<br>1. 持久节点（Persistent）：创建后会一直存在，除非被主动删除。<br>2. 临时节点（Ephemeral）：生命周期与客户端会话绑定，会话结束后节点会自动删除。<br>3. 持久顺序节点（Persistent Sequential）：特性与持久节点类似，但在创建时Zookeeper会自动在其名称后追加一个递增的序号。<br>4. 临时顺序节点（Ephemeral Sequential）：特性与临时节点类似，同时也会被自动追加一个递增的序号。</div>
          </div>
          <div class="card-source">来源: 4.2 znode类型</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">Zookeeper的监听通知机制（watch）是如何工作的？它在配置管理中有什么应用？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">客户端可以对Zookeeper上的某个节点（znode）设置watch（监听）。当该节点发生变动时，客户端会收到一个包含变动事件和新内容的通知。在配置管理中，可以将所有服务的配置信息存储在一个特定的znode上，并让所有服务器实例监听该节点。当需要更新配置时，只需修改znode的值，所有服务器都会收到通知并获取新的配置信息。</div>
          </div>
          <div class="card-source">来源: 4.3 Zookeeper的监听通知机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">架构</div>
          <div class="card-question">在Zookeeper集群中，节点扮演哪三种不同的角色？请分别描述它们的功能。</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">架构</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">Zookeeper集群中的节点角色分为三种：<br>1.  **Leader**：是事务请求（写请求）的唯一处理者，同时也可以处理读请求。<br>2.  **Follower**：可以直接处理读请求，但会将事务请求转发给Leader处理，并参与Leader选举。<br>3.  **Observer**：类似于Follower，可以处理读请求以提高集群吞吐量，但不参与Leader选举和提案表决，可以看作是“临时工”。</div>
          </div>
          <div class="card-source">来源: 6.2 Zookeeper集群中节点的角色</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">Zookeeper集群的Leader选举规则是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">Leader的选举主要依据两个ID：zxid和myid。<br>1. 首先，比较各个节点的zxid（事务ID），zxid最大的节点将被选举为Leader，因为zxid越大代表数据越新。<br>2. 如果多个节点的zxid相同，则会比较它们的myid（服务器唯一标识），myid最大的节点将被选举为Leader。</div>
          </div>
          <div class="card-source">来源: 6.4 Zookeeper选举</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">什么是Zookeeper的“过半数存活原则”？为什么需要这个机制？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">“过半数存活原则”指Zookeeper集群中必须有超过一半的机器存活，整个集群才能正常对外提供服务。这个机制主要是为了防止“脑裂”问题。在网络分区的情况下，集群可能被分割成多个小集群，如果没有此原则，每个小集群都可能选举出自己的Leader，导致数据不一致。过半数机制确保了在任何时刻整个系统中最多只有一个分区能够选举出Leader并正常工作，从而保证了数据的一致性。</div>
          </div>
          <div class="card-source">来源: 6.6 Zookeeper过半数存活原则 & 6.7 为什么Zookeeper需要设计一个过半数存活机制?</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">Zookeeper如何应用于分布式锁的实现？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">可以将Zookeeper上的一个znode视为一把锁。所有需要获取锁的客户端都尝试去创建一个特定的znode（例如 /distribute_lock）。由于Zookeeper的特性，只有一个客户端能够成功创建该节点，这个客户端就获得了锁。当该客户端使用完锁后，通过删除自己创建的znode来释放锁，其他客户端便可以重新竞争创建该节点。</div>
          </div>
          <div class="card-source">来源: 8.3 分布式锁</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">Zookeeper是如何支持服务发现的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">服务提供者启动时，会在Zookeeper的一个预定父节点下（如/service/business/what）注册一个临时的子节点，节点中存储自己的服务地址和端口信息。服务消费者则通过监听该父节点下的子节点列表来获取所有可用的服务。因为注册的节点是临时的，当服务提供者宕机或下线时，其对应的临时节点会自动被删除，消费者能及时发现并停止向其发送请求，从而实现了动态的服务发现。</div>
          </div>
          <div class="card-source">来源: 8.5 发现服务</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">描述Zookeeper集群中数据同步（写操作）的流程。</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">数据同步流程如下：<br>1. 写请求首先被发送给Leader。<br>2. Leader收到请求后，会向所有Follower节点广播一个写操作提案。<br>3. 每个节点在本地执行写操作，并向Leader汇报是否成功。<br>4. 当Leader收到超过半数节点的成功反馈后，它会向所有节点广播一个“提交事务”的指令。<br>5. 各节点收到提交指令后，正式完成写操作。</div>
          </div>
          <div class="card-source">来源: 6.3 Zookeeper数据同步</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
